-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[LLVM][CLANG] Update signal-handling behavior to comply with POSIX #169340
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@llvm/pr-subscribers-clangd @llvm/pr-subscribers-clang-driver Author: Xing Xue (xingxue-ibm) ChangesThe POSIX standard POSIX.1-2024 specifies how the utility reacts to signals as follows. This PR updates the LLVM/Clang’s behavior accordingly by not installing a signal handler when the inherited disposition is <signal.h> specifies the default action of signals. Full diff: https://github.com/llvm/llvm-project/pull/169340.diff 14 Files Affected:
diff --git a/clang-tools-extra/clangd/unittests/ThreadCrashReporterTests.cpp b/clang-tools-extra/clangd/unittests/ThreadCrashReporterTests.cpp
index 0eb8d472c6702..1c86fcbea4a4b 100644
--- a/clang-tools-extra/clangd/unittests/ThreadCrashReporterTests.cpp
+++ b/clang-tools-extra/clangd/unittests/ThreadCrashReporterTests.cpp
@@ -13,6 +13,17 @@
#include <csignal>
#include <string>
+// According to the POSIX specification, if the inherited disposition of a
+// signal is the default action, the behavior of utilitys must be as if the
+// default action had been taken. When the required default action is to
+// terminate the process, such as for SIGUSR1, the utility may catch the
+// signal, perform additional processing, restore the default disposition,
+// and then re-signal itself. This causes the process to terminate as
+// required. Because of this behavior, the crash-reporter test here is not
+// suitable for Unix platforms.
+
+#ifndef LLVM_ON_UNIX
+
namespace clang {
namespace clangd {
@@ -76,3 +87,4 @@ TEST(ThreadCrashReporterTest, All) {
} // namespace
} // namespace clangd
} // namespace clang
+#endif // !LLVM_ON_UNIX
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index c6dd66d9efef3..85fc200d88169 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -2298,7 +2298,14 @@ int Driver::ExecuteCompilation(
if (!FailingCommand->getCreator().hasGoodDiagnostics() || CommandRes != 1) {
// FIXME: See FIXME above regarding result code interpretation.
+#if LLVM_ON_UNIX
+ // On Unix, signals are represented by return codes of 128 plus the
+ // signal number. Return code 255 is excluded because some tools,
+ // such as llvm-ifs, exit with code 255 (-1) on failure.
+ if (CommandRes > 128 && CommandRes != 255)
+#else
if (CommandRes < 0)
+#endif
Diag(clang::diag::err_drv_command_signalled)
<< FailingTool.getShortName();
else
diff --git a/clang/test/Driver/crash-diagnostics-dir-3.c b/clang/test/Driver/crash-diagnostics-dir-3.c
index a91bc48d7e462..8d22df85abd2f 100644
--- a/clang/test/Driver/crash-diagnostics-dir-3.c
+++ b/clang/test/Driver/crash-diagnostics-dir-3.c
@@ -1,6 +1,6 @@
// RUN: export LSAN_OPTIONS=detect_leaks=0
// RUN: rm -rf %t
-// RUN: not env CLANG_CRASH_DIAGNOSTICS_DIR=%t %clang -c %s -o - 2>&1 | FileCheck %s
+// RUN: not --crash env CLANG_CRASH_DIAGNOSTICS_DIR=%t %clang -c %s -o - 2>&1 | FileCheck %s
#pragma clang __debug parser_crash
// CHECK: Preprocessed source(s) and associated run script(s) are located at:
// CHECK: diagnostic msg: {{.*}}{{/|\\}}crash-diagnostics-dir-3.c.tmp{{(/|\\).*}}.c
diff --git a/clang/test/Driver/crash-diagnostics-dir.c b/clang/test/Driver/crash-diagnostics-dir.c
index 16382eff1cde7..56c77beae7234 100644
--- a/clang/test/Driver/crash-diagnostics-dir.c
+++ b/clang/test/Driver/crash-diagnostics-dir.c
@@ -1,6 +1,6 @@
// RUN: export LSAN_OPTIONS=detect_leaks=0
// RUN: rm -rf %t
-// RUN: not %clang -fcrash-diagnostics-dir=%t -c %s -o - 2>&1 | FileCheck %s
+// RUN: not --crash %clang -fcrash-diagnostics-dir=%t -c %s -o - 2>&1 | FileCheck %s
#pragma clang __debug parser_crash
// CHECK: Preprocessed source(s) and associated run script(s) are located at:
// CHECK: diagnostic msg: {{.*}}{{/|\\}}crash-diagnostics-dir.c.tmp{{(/|\\).*}}.c
diff --git a/clang/test/Driver/crash-report-clang-cl.cpp b/clang/test/Driver/crash-report-clang-cl.cpp
index 963c3b6d0ab03..3efeb8088db52 100644
--- a/clang/test/Driver/crash-report-clang-cl.cpp
+++ b/clang/test/Driver/crash-report-clang-cl.cpp
@@ -2,7 +2,7 @@
// RUN: rm -rf %t
// RUN: mkdir %t
-// RUN: not %clang_cl -fsyntax-only /Brepro /source-charset:utf-8 \
+// RUN: not --crash %clang_cl -fsyntax-only /Brepro /source-charset:utf-8 \
// RUN: -fcrash-diagnostics-dir=%t -- %s 2>&1 | FileCheck %s
// RUN: cat %t/crash-report-clang-cl-*.cpp | FileCheck --check-prefix=CHECKSRC %s
// RUN: cat %t/crash-report-clang-cl-*.sh | FileCheck --check-prefix=CHECKSH %s
diff --git a/clang/test/Driver/crash-report-header.h b/clang/test/Driver/crash-report-header.h
index 04865a0cc300f..31c71ff625c05 100644
--- a/clang/test/Driver/crash-report-header.h
+++ b/clang/test/Driver/crash-report-header.h
@@ -1,7 +1,7 @@
// RUN: export LSAN_OPTIONS=detect_leaks=0
// RUN: rm -rf %t
// RUN: mkdir %t
-// RUN: env TMPDIR="%t" TEMP="%t" TMP="%t" RC_DEBUG_OPTIONS=1 not %clang -fsyntax-only %s 2>&1 | FileCheck %s
+// RUN: env TMPDIR="%t" TEMP="%t" TMP="%t" RC_DEBUG_OPTIONS=1 not --crash %clang -fsyntax-only %s 2>&1 | FileCheck %s
// RUN: cat %t/crash-report-header-*.h | FileCheck --check-prefix=CHECKSRC "%s"
// RUN: cat %t/crash-report-header-*.sh | FileCheck --check-prefix=CHECKSH "%s"
// REQUIRES: crash-recovery
diff --git a/clang/test/Driver/crash-report-spaces.c b/clang/test/Driver/crash-report-spaces.c
index b4d8ac1f57e83..c01c8df0552de 100644
--- a/clang/test/Driver/crash-report-spaces.c
+++ b/clang/test/Driver/crash-report-spaces.c
@@ -2,7 +2,7 @@
// RUN: rm -rf "%t"
// RUN: mkdir "%t"
// RUN: cp "%s" "%t/crash report spaces.c"
-// RUN: env TMPDIR="%t" TEMP="%t" TMP="%t" RC_DEBUG_OPTIONS=1 not %clang -fsyntax-only "%t/crash report spaces.c" 2>&1 | FileCheck "%s"
+// RUN: env TMPDIR="%t" TEMP="%t" TMP="%t" RC_DEBUG_OPTIONS=1 not --crash %clang -fsyntax-only "%t/crash report spaces.c" 2>&1 | FileCheck "%s"
// RUN: cat "%t/crash report spaces"-*.c | FileCheck --check-prefix=CHECKSRC "%s"
// RUN: cat "%t/crash report spaces"-*.sh | FileCheck --check-prefix=CHECKSH "%s"
// REQUIRES: crash-recovery
diff --git a/clang/test/Driver/crash-report-with-asserts.c b/clang/test/Driver/crash-report-with-asserts.c
index 686c49f339fb7..0b619d9ec2f46 100644
--- a/clang/test/Driver/crash-report-with-asserts.c
+++ b/clang/test/Driver/crash-report-with-asserts.c
@@ -12,13 +12,13 @@
// RUN: env TMPDIR=%t TEMP=%t TMP=%t RC_DEBUG_OPTIONS=1 \
// RUN: CC_PRINT_HEADERS=1 CC_LOG_DIAGNOSTICS=1 \
-// RUN: not %clang %s @%t.rsp -DASSERT 2>&1 | FileCheck %s
+// RUN: not --crash %clang %s @%t.rsp -DASSERT 2>&1 | FileCheck %s
// RUN: cat %t/crash-report-*.c | FileCheck --check-prefix=CHECKSRC %s
// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH %s
// RUN: env TMPDIR=%t TEMP=%t TMP=%t RC_DEBUG_OPTIONS=1 \
// RUN: CC_PRINT_HEADERS=1 CC_LOG_DIAGNOSTICS=1 \
-// RUN: not %clang %s @%t.rsp -DUNREACHABLE 2>&1 | FileCheck %s
+// RUN: not --crash %clang %s @%t.rsp -DUNREACHABLE 2>&1 | FileCheck %s
// RUN: cat %t/crash-report-with-asserts-*.c | FileCheck --check-prefix=CHECKSRC %s
// RUN: cat %t/crash-report-with-asserts-*.sh | FileCheck --check-prefix=CHECKSH %s
diff --git a/clang/test/Driver/crash-report.cpp b/clang/test/Driver/crash-report.cpp
index 59eee65af57ee..4b5adfc02bff4 100644
--- a/clang/test/Driver/crash-report.cpp
+++ b/clang/test/Driver/crash-report.cpp
@@ -12,13 +12,13 @@
// RUN: env TMPDIR=%t TEMP=%t TMP=%t RC_DEBUG_OPTIONS=1 \
// RUN: CC_PRINT_HEADERS=1 CC_LOG_DIAGNOSTICS=1 \
-// RUN: not %clang %s @%t.rsp -DPARSER 2>&1 | FileCheck %s
+// RUN: not --crash %clang %s @%t.rsp -DPARSER 2>&1 | FileCheck %s
// RUN: cat %t/crash-report-*.cpp | FileCheck --check-prefix=CHECKSRC %s
// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH %s
// RUN: env TMPDIR=%t TEMP=%t TMP=%t RC_DEBUG_OPTIONS=1 \
// RUN: CC_PRINT_HEADERS=1 CC_LOG_DIAGNOSTICS=1 \
-// RUN: not %clang %s @%t.rsp -DCRASH 2>&1 | FileCheck %s
+// RUN: not --crash %clang %s @%t.rsp -DCRASH 2>&1 | FileCheck %s
// RUN: cat %t/crash-report-*.cpp | FileCheck --check-prefix=CHECKSRC %s
// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH %s
diff --git a/clang/test/Driver/emit-reproducer.c b/clang/test/Driver/emit-reproducer.c
index 18e1b4e41b91d..28c585193a7d0 100644
--- a/clang/test/Driver/emit-reproducer.c
+++ b/clang/test/Driver/emit-reproducer.c
@@ -3,13 +3,13 @@
// RUN: echo "%s -fcrash-diagnostics-dir=%t -fsyntax-only" | sed -e 's/\\/\\\\/g' > %t.rsp
-// RUN: not %clang -DFATAL @%t.rsp -gen-reproducer=off 2>&1 | FileCheck %s --check-prefix=NOT
-// RUN: not %clang -DFATAL @%t.rsp -fno-crash-diagnostics 2>&1 | FileCheck %s --check-prefix=NOT
-// RUN: not %clang -DFATAL @%t.rsp 2>&1 | FileCheck %s
-// RUN: not %clang -DFATAL @%t.rsp -gen-reproducer=crash 2>&1 | FileCheck %s
-// RUN: not %clang -DFATAL @%t.rsp -gen-reproducer=error 2>&1 | FileCheck %s
-// RUN: not %clang -DFATAL @%t.rsp -gen-reproducer=always 2>&1 | FileCheck %s
-// RUN: not %clang -DFATAL @%t.rsp -gen-reproducer 2>&1 | FileCheck %s
+// RUN: not --crash %clang -DFATAL @%t.rsp -gen-reproducer=off 2>&1 | FileCheck %s --check-prefix=NOT
+// RUN: not --crash %clang -DFATAL @%t.rsp -fno-crash-diagnostics 2>&1 | FileCheck %s --check-prefix=NOT
+// RUN: not --crash %clang -DFATAL @%t.rsp 2>&1 | FileCheck %s
+// RUN: not --crash %clang -DFATAL @%t.rsp -gen-reproducer=crash 2>&1 | FileCheck %s
+// RUN: not --crash %clang -DFATAL @%t.rsp -gen-reproducer=error 2>&1 | FileCheck %s
+// RUN: not --crash %clang -DFATAL @%t.rsp -gen-reproducer=always 2>&1 | FileCheck %s
+// RUN: not --crash %clang -DFATAL @%t.rsp -gen-reproducer 2>&1 | FileCheck %s
// RUN: not %clang -DERROR @%t.rsp -gen-reproducer=off 2>&1 | FileCheck %s --check-prefix=NOT
// RUN: not %clang -DERROR @%t.rsp -fno-crash-diagnostics 2>&1 | FileCheck %s --check-prefix=NOT
diff --git a/clang/test/Driver/output-file-cleanup.c b/clang/test/Driver/output-file-cleanup.c
index 3628df8192652..b5f548dc0b01a 100644
--- a/clang/test/Driver/output-file-cleanup.c
+++ b/clang/test/Driver/output-file-cleanup.c
@@ -2,7 +2,7 @@
// RUN: rm -f "%t.d" "%t1.s" "%t2.s" "%t3.s" "%t4.s" "%t5.s"
//
// RUN: touch %t.s
-// RUN: not %clang -S -DCRASH -o %t.s -MMD -MF %t.d %s
+// RUN: not --crash %clang -S -DCRASH -o %t.s -MMD -MF %t.d %s
// RUN: test ! -f %t.s
// RUN: test ! -f %t.d
diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp
index 1e2c9884ba63d..262043bbb78ad 100644
--- a/clang/tools/driver/driver.cpp
+++ b/clang/tools/driver/driver.cpp
@@ -54,6 +54,9 @@
#include <optional>
#include <set>
#include <system_error>
+#if LLVM_ON_UNIX
+#include <signal.h>
+#endif
using namespace clang;
using namespace clang::driver;
@@ -400,6 +403,7 @@ int clang_main(int Argc, char **Argv, const llvm::ToolContext &ToolContext) {
Driver::CommandStatus CommandStatus = Driver::CommandStatus::Ok;
// Pretend the first command failed if ReproStatus is Always.
const Command *FailingCommand = nullptr;
+ int CommandRes = 0;
if (!C->getJobs().empty())
FailingCommand = &*C->getJobs().begin();
if (C && !C->containsError()) {
@@ -407,7 +411,7 @@ int clang_main(int Argc, char **Argv, const llvm::ToolContext &ToolContext) {
Res = TheDriver.ExecuteCompilation(*C, FailingCommands);
for (const auto &P : FailingCommands) {
- int CommandRes = P.first;
+ CommandRes = P.first;
FailingCommand = P.second;
if (!Res)
Res = CommandRes;
@@ -464,6 +468,18 @@ int clang_main(int Argc, char **Argv, const llvm::ToolContext &ToolContext) {
Res = 1;
#endif
+#if LLVM_ON_UNIX
+ // On Unix, signals are represented by return codes of 128 plus the signal
+ // number. If the return code indicates it was from a signal handler, raise
+ // the signal so that the exit code includes the signal number, as required
+ // by POSIX. Return code 255 is excluded because some tools, such as
+ // llvm-ifs, exit with code 255 (-1) on failure.
+ if (CommandRes > 128 && CommandRes != 255) {
+ llvm::sys::unregisterHandlers();
+ raise(CommandRes - 128);
+ }
+#endif
+
// If we have multiple failing commands, we return the result of the first
// failing command.
return Res;
diff --git a/llvm/lib/Support/CrashRecoveryContext.cpp b/llvm/lib/Support/CrashRecoveryContext.cpp
index 1ba0c2383daec..c6bfa4d0245da 100644
--- a/llvm/lib/Support/CrashRecoveryContext.cpp
+++ b/llvm/lib/Support/CrashRecoveryContext.cpp
@@ -398,7 +398,11 @@ static void installExceptionOrSignalHandlers() {
sigemptyset(&Handler.sa_mask);
for (unsigned i = 0; i != NumSignals; ++i) {
- sigaction(Signals[i], &Handler, &PrevActions[i]);
+ struct sigaction act;
+ sigaction(Signals[i], NULL, &act);
+ // Don't install signal handler if the disposition of a signal is SIG_IGN.
+ if (act.sa_handler != SIG_IGN)
+ sigaction(Signals[i], &Handler, &PrevActions[i]);
}
}
diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc
index 78d6540db98a7..7e674eba01c4e 100644
--- a/llvm/lib/Support/Unix/Signals.inc
+++ b/llvm/lib/Support/Unix/Signals.inc
@@ -327,8 +327,12 @@ static void RegisterHandlers() { // Not signal-safe.
}
sigemptyset(&NewHandler.sa_mask);
- // Install the new handler, save the old one in RegisteredSignalInfo.
- sigaction(Signal, &NewHandler, &RegisteredSignalInfo[Index].SA);
+ // Install the new handler if the signal disposition isn't SIG_IGN,
+ // save the old one in RegisteredSignalInfo.
+ struct sigaction act;
+ sigaction(Signal, NULL, &act);
+ if (act.sa_handler != SIG_IGN)
+ sigaction(Signal, &NewHandler, &RegisteredSignalInfo[Index].SA);
RegisteredSignalInfo[Index].SigNo = Signal;
++NumRegisteredSignals;
};
@@ -408,14 +412,12 @@ static void SignalHandler(int Sig, siginfo_t *Info, void *) {
// Otherwise if it is a fault (like SEGV) run any handler.
llvm::sys::RunSignalHandlers();
-#ifdef __s390__
- // On S/390, certain signals are delivered with PSW Address pointing to
- // *after* the faulting instruction. Simply returning from the signal
- // handler would continue execution after that point, instead of
- // re-raising the signal. Raise the signal manually in those cases.
- if (Sig == SIGILL || Sig == SIGFPE || Sig == SIGTRAP)
- raise(Sig);
-#endif
+ // Resignal if it is a kill signal so that the exit code contains the
+ // terminating signal number.
+ if (llvm::is_contained(KillSigs, Sig)) {
+ raise(Sig); // Execute the default handler.
+ return;
+ }
#if defined(__linux__)
// Re-raising a signal via `raise` loses the original siginfo. Recent
@@ -438,6 +440,11 @@ static void InfoSignalHandler(int Sig) {
SaveAndRestore SaveErrnoDuringASignalHandler(errno);
if (SignalHandlerFunctionType CurrentInfoFunction = InfoSignalFunction)
CurrentInfoFunction();
+
+ if (Sig == SIGUSR1) {
+ sys::unregisterHandlers();
+ raise(Sig);
+ }
}
void llvm::sys::RunInterruptHandlers() { RemoveFilesToRemove(); }
|
…N, resignal after the signal handler handles a signal so the exit code of the compiler contains the signal number.
4acec9c to
b3d9e4b
Compare
The POSIX standard POSIX.1-2024 specifies how the utility reacts to signals as follows.
This PR updates the LLVM/Clang’s behavior accordingly by not installing a signal handler when the inherited disposition is
SIG_IGN, and by ensuring that the exit code reflects the terminating signal number, resignaling after the signal is handled. Additionally, theclangdunit testThreadCrashReporterTestis excluded on Unix platforms because it expects execution to continue after returning from theSIGUSR1signal handler.<signal.h> specifies the default action of signals.